import { ExclamationCircleOutlined } from '@ant-design/icons';
import { Modal, notification } from 'antd';
import lodash from 'lodash';
import React from 'react';
import { authTokenKey, redirectLogin } from './authentication';

export class ApiError extends Error {
  body: Record<string, any>;

  constructor(message: string, body: Record<string, any>) {
    super(message);
    this.body = body;
  }
}

const controller = new AbortController();
const Timeout_Time = 50000; //超时时间

const CODE_MESSAGE: any = {
  200: '服务器成功返回请求的数据',
  201: '新建或修改数据成功',
  202: '一个请求已经进入后台排队（异步任务）',
  204: '删除数据成功',
  400: '发出的请求有错误，服务器没有进行新建或修改数据的操作',
  401: '用户没有权限（令牌错误）',
  403: '用户得到授权，但是访问是被禁止的',
  404: '发出的请求针对的是不存在的记录，服务器没有进行操作',
  405: '请求方法不被允许',
  406: '请求的格式不可得',
  410: '请求的资源被永久删除，且不会再得到的',
  422: '当创建一个对象时，发生一个验证错误',
  500: '服务器发生错误，请检查服务器',
  502: '网关错误',
  503: '服务不可用，服务器暂时过载或维护',
  504: '网关超时',
};
//超时取消请求,返回错误码
const timeoutPromise = (timeout: number) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(new Response('timeout', { status: 504 }));
      controller.abort();
    }, timeout);
  });
};

export const fetcher = (url: string, options: any = {}): Promise<any> => {
  const throwError = options.throwError || false;
  options.method = 'POST';
  options.headers = {
    ...options.headers,
    'Content-Type': 'application/json;charset=utf-8',
  };

  const token = window.localStorage.getItem(authTokenKey);
  if (token) {
    options.headers = {
      ...options.headers,
      'x-auth-token': token,
    };
  }

  const nulledBody = lodash.mapValues(options.body || {}, (value) =>
    value === undefined ? null : value,
  );
  const body = JSON.stringify(nulledBody);

  if (window.sessionExpire) {
    // 如果 token 已过期不再请求接口
    const message401 = CODE_MESSAGE[401];
    const errorBody = {
      status: 401,
      error: message401,
      message: message401,
    };
    if (throwError) {
      throw new ApiError(message401, errorBody);
    } else {
      return Promise.resolve({ error: errorBody });
    }
  }

  return Promise.race([
    timeoutPromise(Timeout_Time),
    fetch(url, { ...options, body }),
  ])
    .then(async (response: any) => {
      // console.debug('fetch response, ', response);
      const { status } = response;

      if (status >= 400) {
        let responseBody = await response.text();
        try {
          responseBody = JSON.parse(responseBody);
        } catch {}
        const defaultMsg = CODE_MESSAGE[response?.status];
        if (status === 504 || status === 503 || status === 502) {
          // 以上错误没有消息体，直接抛出错误
          const errorBody = {
            status,
            error: defaultMsg,
            message: responseBody,
          };
          if (throwError) {
            throw new ApiError(defaultMsg, errorBody);
          } else {
            return Promise.resolve({ error: errorBody });
          }
        }

        if (status === 401) {
          // 保证过期弹窗只弹出一次
          if (!window.sessionExpire) {
            Modal.confirm({
              centered: true,
              title: '登录状态已过期，请重新登录',
              icon: <ExclamationCircleOutlined />,
              okText: '重新登录',
              cancelText: '取消',
              onOk() {
                sessionStorage.clear();
                window.localStorage.removeItem(authTokenKey);
                redirectLogin(true);
              },
            });
            window.sessionExpire = true;
          }
        }

        const bodyMessage = responseBody.message || defaultMsg;
        // 业务服务错误，有消息体
        const errorBody = {
          ...responseBody,
          message: bodyMessage,
        };
        if (throwError) {
          throw new ApiError(bodyMessage, errorBody);
        } else {
          return Promise.resolve({ error: errorBody });
        }
      }

      return response.text().then((dataText: string) => {
        return dataText.length === 0 ? null : JSON.parse(dataText);
      });
    })
    .catch((err: ApiError) => {
      console.warn('接口错误:', err, err.body);
      if (!navigator.onLine) {
        const reason = '您的网络发生异常，无法连接服务器';
        notification.error({
          message: '网络异常',
          description: reason,
        });
      } else {
        // const message = '系统繁忙，请稍后再试！';
        // const errorBody = {
        //   status: '5xx', error: err.toString(), message
        // }
        // TODO 优化错误显示
        // notification.error({
        //   message: err.body.error,
        //   description: err.body.message,
        // });
        // return Promise.resolve({ error: err.body });
      }
      throw err;
    });
};
